﻿using mozhi.smarterp.Finance.Accounts.Repository;
using mozhi.smarterp.Finance.Basedatas.Repository;
using mozhi.smarterp.Finance.Enums;
using mozhi.smarterp.Finance.Files.Repository;
using mozhi.smarterp.Finance.Logs.Repository;
using Mozhi.Abp.Domain.BasicData.Repositories;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Domain.Services;
using Volo.Abp.Guids;
using Volo.Abp.Users;

namespace mozhi.smarterp.Finance.Logs.Services
{
    public class OperationLogManager : DomainService
    {
        private IOperationLogRepository _repository;
        private IOperationLogSettingRepository _logSettingRepository;
        private IGuidGenerator _guidGenerator;
        private ICurrentUser _currentUser;
        private ISupplierRepository _supplierRepository;
        private IAccountRepository _accountRepository;
        private IFileRepository _fileRepository;
        private ICurrencyRepository _currencyRepository;

        public OperationLogManager(IOperationLogRepository repository, IGuidGenerator guidGenerator, IServiceProvider serviceProvider)
        {
            _repository = repository;
            _logSettingRepository = serviceProvider.GetService(typeof(IOperationLogSettingRepository)) as IOperationLogSettingRepository;
            _currentUser = serviceProvider.GetService(typeof(ICurrentUser)) as ICurrentUser;
            _supplierRepository = serviceProvider.GetService(typeof(ISupplierRepository)) as ISupplierRepository;
            _guidGenerator = guidGenerator;
            _accountRepository = serviceProvider.GetService(typeof(IAccountRepository)) as IAccountRepository;
            _fileRepository = serviceProvider.GetService(typeof(IFileRepository)) as IFileRepository;
            _currencyRepository = serviceProvider.GetService(typeof(ICurrencyRepository)) as ICurrencyRepository;
        }

        public async Task<OperationLog> LogAsync(string templateCode, OperationType operationType, object oldObject, object newObject)
        {
            var setting = await _logSettingRepository.FindAsync(templateCode);

            if (setting == null)
                throw new BusinessException("100000", $"无法获取日志配置:{templateCode}");

            var properties = oldObject.GetType().GetProperties();

            var idProperty = properties.FirstOrDefault(x => x.Name == "Id");

            var idPropertyValue = idProperty.GetValue(oldObject);

            Guid? id = idProperty == null || idPropertyValue == null ? null : Guid.Parse(idPropertyValue.ToString());

            var nameProperty = properties.FirstOrDefault(x => x.Name == "Name");

            var namePropertypeValue = nameProperty == null ? null : nameProperty.GetValue(oldObject);

            var codeProperty = properties.FirstOrDefault(x => x.Name == "Code");

            var codePropertyValue = codeProperty == null ? null : codeProperty.GetValue(oldObject);

            var log = new OperationLog(_guidGenerator.Create(), _currentUser?.Id, _currentUser?.UserName, operationType, setting.Code, setting.Name, id ?? default, $"{codePropertyValue} {namePropertypeValue}", "");

            if (operationType == OperationType.Update)
                await SetLogItemsAsync(log, setting, oldObject, newObject, properties.ToList());

            log = await _repository.InsertAsync(log);

            return log;

        }

        #region
        private async Task SetLogItemsAsync(OperationLog log, OperationLogSetting setting, object oldObject, object newObject, List<PropertyInfo> properties)
        {
            if (properties == null || properties.Count == 0)
                return;

            var items = new List<OperationLogItem>();

            foreach (var property in properties)
            {
                if (property.PropertyType.IsGenericType && property.PropertyType.GetGenericTypeDefinition() == typeof(IList<>))
                {
                    //列表类型
                    var oldValues = property.GetValue(oldObject);
                    var newValues = property.GetValue(newObject);

                    if (oldValues == null && newValues == null)
                        continue;

                    var oldValuesType = oldValues.GetType();
                    var oldValuesCount = Convert.ToInt32(oldValuesType.GetProperty("Count").GetValue(oldValues, null));

                    var newValuesType = newValues.GetType();
                    var newValuesCount = Convert.ToInt32(newValuesType.GetProperty("Count").GetValue(newValues, null));

                    var defferentCount = oldValuesCount - newValuesCount;

                    if (defferentCount != 0)
                    {
                        var fieldCaption = "分录";

                        fieldCaption += defferentCount > 0 ? "移除" : "新增";

                        var logItem = new OperationLogItem(_guidGenerator.Create(), log.Id, property.Name, fieldCaption,
                            oldValuesCount, oldValuesCount.ToString(), newValuesCount, newValuesCount.ToString(), null);

                        items.Add(logItem);
                    }
                    else
                    {
                        var itemProperties = oldValuesType.GetProperties();

                        for (var i = 0; i < oldValuesCount; i++)
                        {
                            var oldItem = oldValuesType.GetProperty("Item").GetValue(oldValues, new object[] { i });

                            var itemId = oldItem.GetType().GetProperty("Id").GetValue(oldItem, null);

                            var matchNewItem = GetMatchNewItem(itemId, newValuesType, newValues, newValuesCount);

                            //找不到匹配的项目
                            if (matchNewItem == null)
                                continue;

                            foreach (var itemProperty in itemProperties)
                            {
                                var logItem = await GetOperationLogItemAsync(log.Id, oldItem, matchNewItem, itemProperty, setting);

                                if (logItem != null)
                                    items.Add(logItem);
                            }
                        }
                    }
                }
                else
                {
                    var logItem = await GetOperationLogItemAsync(log.Id, oldObject, newObject, property, setting);

                    if (logItem != null)
                        items.Add(logItem);
                }
            }

            log.ChangeItems(items);
        }

        private async Task<string> GetFieldValueCaptionAsync(int? fieldType, object fieldValue)
        {
            if (!fieldType.HasValue || fieldValue == null)
                return null;

            if (fieldType == 5)
                return fieldValue.ToString() == "BusinessEnterprises" ? "企业会计准则" : "小企业会计准则";

            Guid objectId;

            //无法转换为guid
            if (!Guid.TryParse(fieldValue.ToString(), out objectId))
                return null;

            if (fieldType == 1)
                return _currentUser.Name;

            if (fieldType == 2)
            {
                var supplier = await _supplierRepository.GetAsync(objectId);

                return supplier?.Code + " " + supplier?.Name;
            }

            if (fieldType == 3)
            {
                var account = await _accountRepository.FindAsync(objectId);

                return account?.FullName;
            }

            if (fieldType == 4)
            {
                var file = await _fileRepository.FindAsync(objectId);

                return file?.FileName + file?.FileExtension;
            }

            if(fieldType==6)
            {
                var currency = await _currencyRepository.GetAsync(objectId);

                return currency?.Name;
            }

            return null;
        }

        private object GetMatchNewItem(object id, Type newValuesType, object newValues, int newValuesCount)
        {
            object result = null;

            for (var i = 0; i < newValuesCount; i++)
            {
                var item = newValuesType.GetProperty("Item").GetValue(newValues, new object[] { i });

                var itemId = item.GetType().GetProperty("Id").GetValue(item, null);

                if (itemId == id)
                {
                    result = item;
                    break;
                }
            }

            return result;
        }

        private async Task<OperationLogItem> GetOperationLogItemAsync(Guid logId, object oldObject, object newObject, PropertyInfo property, OperationLogSetting setting)
        {
            OperationLogItem result = null;

            var matchTemplateItem = setting.Items.FirstOrDefault(x => x.FieldName == property.Name);

            //模板没有的字段就不记录了
            if (matchTemplateItem == null)
                return result;


            var oldValue = property.GetValue(oldObject);
            var newValue = property.GetValue(newObject);

            if ((oldValue == null && newValue == null) || (oldValue != null && oldValue.Equals(newValue)))
                return result;

            var fieldCaption = matchTemplateItem?.FieldCaption;

            var fieldType = matchTemplateItem?.FieldType;

            var oldValueCaption = await GetFieldValueCaptionAsync(fieldType, oldValue);

            var newValueCaption = await GetFieldValueCaptionAsync(fieldType, newValue);

            result = new OperationLogItem(_guidGenerator.Create(), logId, property.Name, fieldCaption, oldValue, oldValueCaption, newValue, newValueCaption, null);

            return result;
        }

        #endregion
    }



}
